package com.marvhong.videoeditor.crop.activity;

import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Message;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.VideoView;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.marvhong.videoeditor.R;
import com.marvhong.videoeditor.VideoEditFun;
import com.marvhong.videoeditor.crop.adapter.TrimVideoAdapter;
import com.marvhong.videoeditor.crop.base.LibVideoBaseCropActivity;
import com.marvhong.videoeditor.crop.model.VideoEditInfo;
import com.marvhong.videoeditor.crop.utils.ExtractFrameWorkThread;
import com.marvhong.videoeditor.crop.utils.ExtractVideoInfoUtil;
import com.marvhong.videoeditor.crop.utils.TimeUtils;
import com.marvhong.videoeditor.crop.utils.UIUtils;
import com.marvhong.videoeditor.crop.utils.VideoUtil;
import com.marvhong.videoeditor.dialog.NormalProgressDialog;
import com.marvhong.videoeditor.crop.view.RangeSeekBar;
import com.marvhong.videoeditor.crop.view.VideoThumbSpacingItemDecoration;
import com.north.light.librxffmpeg.FFmpegFunCallback;
import com.north.light.librxffmpeg.FFmpegFunc;

import java.io.File;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * @author LLhon
 * @Project Android-Video-Editor
 * @Package com.marvhong.videoeditor
 * @Date 2018/8/21 17:51
 * @description 裁剪视频界面
 */
public class TrimVideoActivity extends LibVideoBaseCropActivity {
    private static final String TAG = TrimVideoActivity.class.getSimpleName();

    private TextView mTvShootTip;
    private RecyclerView mRecyclerView;
    private ImageView mIvPosition;
    private LinearLayout seekBarLayout;
    private View mViewTrimIndicator;
    private LinearLayout mLlTrimContainer;
    private RangeSeekBar seekBar;
    //video view
    private VideoView mPlayView;
    private LinearLayout mPlayerRoot;
    // 最小剪辑时间3s
    private static final long MIN_CUT_DURATION = 3 * 1000L;
    //视频最多剪切多长时间
    private static final long MAX_CUT_DURATION = 60 * 60 * 1000L;
    //seekBar的区域内一共有多少张图片
    private static final int MAX_COUNT_RANGE = 10;
    //左右两边间距
    private static final int MARGIN = UIUtils.dp2Px(56);
    //可裁剪区域的最大宽度
    private ExtractVideoInfoUtil mExtractVideoInfoUtil;
    private int mMaxWidth;
    //视频总时长
    private long duration;
    private TrimVideoAdapter videoEditAdapter;
    //每毫秒所占的px
    private float averageMsPx;
    //每px所占用的ms毫秒
    private float averagePxMs;
    private String OutPutFileDirPath;
    //视频帧工具类
    private ExtractFrameWorkThread mExtractFrameWorkThread;
    //裁剪视频左边区域的时间位置, 右边时间位置
    private long leftProgress, rightProgress;
    private long scrollPos = 0;
    private int mScaledTouchSlop;
    private int lastScrollX;
    //是否拖动中
    private boolean isSeeking;
    private String mVideoPath;

    private boolean isOverScaledTouchSlop;

    //req res params code
    public static String PARAMS_CROP_RESULT_PATH = "PARAMS_CROP_RESULT_PATH";
    public static String PARAMS_CROP_ORG_PATH = "PARAMS_CROP_ORG_PATH";
    public static int TRIM_VIDEO_REQ = 0x001;
    public static int TRIM_VIDEO_RES = 0x002;

    @Override
    protected int getLayoutId() {
        return R.layout.lib_video_eidt_activity_trim_video;
    }

    @Override
    protected void init() {
        mVideoPath = getIntent().getStringExtra(PARAMS_CROP_ORG_PATH);

        mExtractVideoInfoUtil = new ExtractVideoInfoUtil(mVideoPath);
        mMaxWidth = UIUtils.getScreenWidth() - MARGIN * 2;
        mScaledTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();

        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) {
                e.onNext(mExtractVideoInfoUtil.getVideoLength());
                e.onComplete();
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        subscribe(d);
                    }

                    @Override
                    public void onNext(String s) {
                        duration = Long.valueOf(mExtractVideoInfoUtil.getVideoLength());
                        //矫正获取到的视频时长不是整数问题
                        float tempDuration = duration / 1000f;
                        duration = new BigDecimal(tempDuration).setScale(0, BigDecimal.ROUND_HALF_UP).intValue() * 1000;
                        Log.e(TAG, "视频总时长：" + duration);
                        initEditVideo();
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    /**
     * 初始化video view
     */
    private void initVideoView() {
        //动态把video view添加到布局里面
        try {
            mPlayView = new VideoView(getApplicationContext());
            mPlayerRoot.addView(mPlayView);
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mPlayView.getLayoutParams();
            params.width = LinearLayout.LayoutParams.MATCH_PARENT;
            params.height = LinearLayout.LayoutParams.MATCH_PARENT;
            mPlayView.setLayoutParams(params);
            MediaController mediaController = new MediaController(this);
            mediaController.setVisibility(View.INVISIBLE);
            mPlayView.setMediaController(mediaController);
            mPlayView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    return true;
                }
            });
            mPlayView.setVideoPath(mVideoPath);
            mPlayView.start();
        } catch (Exception e) {

        }
    }

    /**
     * 释放video view
     */
    private void releaseVideoView() {
        try {
            mPlayView.stopPlayback();
            mPlayView.suspend();
            mPlayView.setOnErrorListener(null);
            mPlayView.setOnPreparedListener(null);
            mPlayView.setOnCompletionListener(null);
            mPlayView.setMediaController(null);
            mPlayView = null;
            mPlayerRoot.removeAllViews();
        } catch (Exception e) {

        }

    }

    @Override
    protected void initView() {
        mPlayerRoot = findViewById(R.id.activity_trim_video_content);
        initVideoView();
        mTvShootTip = findViewById(R.id.video_shoot_tip);
        mRecyclerView = findViewById(R.id.video_thumb_listview);
        mIvPosition = findViewById(R.id.positionIcon);
        seekBarLayout = findViewById(R.id.id_seekBarLayout);
        mViewTrimIndicator = findViewById(R.id.view_trim_indicator);
        mLlTrimContainer = findViewById(R.id.ll_trim_container);

        mRecyclerView
                .setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        videoEditAdapter = new TrimVideoAdapter(this, mMaxWidth / 10);
        mRecyclerView.setAdapter(videoEditAdapter);
        mRecyclerView.addOnScrollListener(mOnScrollListener);

    }

    @Override
    protected void initEvent() {
        super.initEvent();
        //裁切tab
        findViewById(R.id.ll_trim_tab).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mViewTrimIndicator.setVisibility(View.VISIBLE);
                mLlTrimContainer.setVisibility(View.VISIBLE);
            }
        });
        //取消
        findViewById(R.id.activity_trim_video_cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        //确认
        findViewById(R.id.activity_trim_video_confirm).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //压缩视频并返回
                trimmerAndCompressVideo();
            }
        });
    }

    private void initEditVideo() {
        //for video edit
        long startPosition = 0;
        long endPosition = duration;
        int thumbnailsCount;
        int rangeWidth;
        boolean isOver_10_s;
        if (endPosition <= MAX_CUT_DURATION) {
            isOver_10_s = false;
            thumbnailsCount = MAX_COUNT_RANGE;
            rangeWidth = mMaxWidth;
        } else {
            isOver_10_s = true;
            thumbnailsCount = (int) (endPosition * 1.0f / (MAX_CUT_DURATION * 1.0f)
                    * MAX_COUNT_RANGE);
            rangeWidth = mMaxWidth / MAX_COUNT_RANGE * thumbnailsCount;
        }
        mRecyclerView.addItemDecoration(new VideoThumbSpacingItemDecoration(MARGIN, thumbnailsCount));
        //init seekBar
        if (isOver_10_s) {
            seekBar = new RangeSeekBar(this, 0L, MAX_CUT_DURATION);
            seekBar.setSelectedMinValue(0L);
            seekBar.setSelectedMaxValue(MAX_CUT_DURATION);
        } else {
            seekBar = new RangeSeekBar(this, 0L, endPosition);
            seekBar.setSelectedMinValue(0L);
            seekBar.setSelectedMaxValue(endPosition);
        }
        seekBar.setMin_cut_time(MIN_CUT_DURATION);//设置最小裁剪时间
        seekBar.setNotifyWhileDragging(true);
        seekBar.setOnRangeSeekBarChangeListener(mOnRangeSeekBarChangeListener);
        seekBarLayout.addView(seekBar);
        Log.d(TAG, "-------thumbnailsCount--->>>>" + thumbnailsCount);
        averageMsPx = duration * 1.0f / rangeWidth * 1.0f;
        Log.d(TAG, "-------rangeWidth--->>>>" + rangeWidth);
        Log.d(TAG, "-------localMedia.getDuration()--->>>>" + duration);
        Log.d(TAG, "-------averageMsPx--->>>>" + averageMsPx);
        OutPutFileDirPath = VideoUtil.getSaveEditThumbnailDir(this);
        int extractW = mMaxWidth / MAX_COUNT_RANGE;
        int extractH = UIUtils.dp2Px(62);
        mExtractFrameWorkThread = new ExtractFrameWorkThread(extractW, extractH, mUIHandler,
                mVideoPath,
                OutPutFileDirPath, startPosition, endPosition, thumbnailsCount);
        mExtractFrameWorkThread.start();
        //init pos icon start
        leftProgress = 0;
        if (isOver_10_s) {
            rightProgress = MAX_CUT_DURATION;
        } else {
            rightProgress = endPosition;
        }
        mTvShootTip.setText(getSelTimeTips(rightProgress / 1000));
        averagePxMs = (mMaxWidth * 1.0f / (rightProgress - leftProgress));
        Log.d(TAG, "------averagePxMs----:>>>>>" + averagePxMs);
    }

    /**
     * 获取截取提示的文字
     */
    private Spanned getSelTimeTips(long time) {
        String htmlStr = "已截取<font color = '#00C2F6'>" + time + "</font>秒的内容";
        return Html.fromHtml(htmlStr);
    }


    private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            Log.d(TAG, "-------newState:>>>>>" + newState);
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                isSeeking = false;
//                videoStart();
            } else {
                isSeeking = true;
                if (isOverScaledTouchSlop) {
                    videoPause();
                }
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            isSeeking = false;
            int scrollX = getScrollXDistance();
            //达不到滑动的距离
            if (Math.abs(lastScrollX - scrollX) < mScaledTouchSlop) {
                isOverScaledTouchSlop = false;
                return;
            }
            isOverScaledTouchSlop = true;
            Log.d(TAG, "-------scrollX:>>>>>" + scrollX);
            //初始状态,why ? 因为默认的时候有56dp的空白！
            if (scrollX == -MARGIN) {
                scrollPos = 0;
            } else {
                // why 在这里处理一下,因为onScrollStateChanged早于onScrolled回调
                videoPause();
                isSeeking = true;
                scrollPos = (long) (averageMsPx * (MARGIN + scrollX));
                Log.d(TAG, "-------scrollPos:>>>>>" + scrollPos);
                leftProgress = seekBar.getSelectedMinValue() + scrollPos;
                rightProgress = seekBar.getSelectedMaxValue() + scrollPos;
                Log.d(TAG, "-------leftProgress:>>>>>" + leftProgress);
                mPlayView.seekTo((int) leftProgress);
            }
            lastScrollX = scrollX;
        }
    };

    /**
     * 水平滑动了多少px
     *
     * @return int px
     */
    private int getScrollXDistance() {
        LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        int position = layoutManager.findFirstVisibleItemPosition();
        View firstVisibleChildView = layoutManager.findViewByPosition(position);
        int itemWidth = firstVisibleChildView.getWidth();
        return (position) * itemWidth - firstVisibleChildView.getLeft();
    }

    private ValueAnimator animator;

    private void anim() {
        Log.d(TAG, "--anim--onProgressUpdate---->>>>>>>" + mPlayView.getCurrentPosition());
        if (mIvPosition.getVisibility() == View.GONE) {
            mIvPosition.setVisibility(View.VISIBLE);
        }
        final LayoutParams params = (LayoutParams) mIvPosition
                .getLayoutParams();
        int start = (int) (MARGIN
                + (leftProgress/*mVideoView.getCurrentPosition()*/ - scrollPos) * averagePxMs);
        int end = (int) (MARGIN + (rightProgress - scrollPos) * averagePxMs);
        animator = ValueAnimator
                .ofInt(start, end)
                .setDuration(
                        (rightProgress - scrollPos) - (leftProgress/*mVideoView.getCurrentPosition()*/
                                - scrollPos));
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                params.leftMargin = (int) animation.getAnimatedValue();
                mIvPosition.setLayoutParams(params);
            }
        });
        animator.start();
    }

    private final MainHandler mUIHandler = new MainHandler(this);

    private static class MainHandler extends Handler {

        private final WeakReference<TrimVideoActivity> mActivity;

        MainHandler(TrimVideoActivity activity) {
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            TrimVideoActivity activity = mActivity.get();
            if (activity != null) {
                if (msg.what == ExtractFrameWorkThread.MSG_SAVE_SUCCESS) {
                    if (activity.videoEditAdapter != null) {
                        VideoEditInfo info = (VideoEditInfo) msg.obj;
                        activity.videoEditAdapter.addItemVideoInfo(info);
                    }
                }
            }
        }
    }

    /**
     * 进度条拖动监听
     */
    private final RangeSeekBar.OnRangeSeekBarChangeListener mOnRangeSeekBarChangeListener = new RangeSeekBar.OnRangeSeekBarChangeListener() {
        @Override
        public void onRangeSeekBarValuesChanged(RangeSeekBar bar, long minValue, long maxValue,
                                                int action, boolean isMin, RangeSeekBar.Thumb pressedThumb) {
            Log.d(TAG, "-----minValue----->>>>>>" + minValue);
            Log.d(TAG, "-----maxValue----->>>>>>" + maxValue);
            leftProgress = minValue + scrollPos;
            rightProgress = maxValue + scrollPos;
            Log.d(TAG, "-----leftProgress----->>>>>>" + leftProgress);
            Log.d(TAG, "-----rightProgress----->>>>>>" + rightProgress);
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.d(TAG, "-----ACTION_DOWN---->>>>>>");
                    isSeeking = false;
                    videoPause();
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.d(TAG, "-----ACTION_MOVE---->>>>>>");
                    isSeeking = true;
                    mPlayView.seekTo((int) (pressedThumb == RangeSeekBar.Thumb.MIN ?
                            leftProgress : rightProgress));
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    Log.d(TAG, "-----ACTION_UP--leftProgress--->>>>>>" + leftProgress);
                    isSeeking = false;
                    //从minValue开始播
                    mPlayView.seekTo((int) leftProgress);
                    videoStart();
                    mTvShootTip.setText(getSelTimeTips((rightProgress - leftProgress) / 1000));
                    break;
                default:
                    break;
            }
        }
    };

    private void videoStart() {
        Log.d(TAG, "----videoStart----->>>>>>>");
        mPlayView.start();
        mIvPosition.clearAnimation();
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
        anim();
        handler.removeCallbacks(run);
        handler.post(run);
    }

    private void videoProgressUpdate() {
        long currentPosition = mPlayView.getCurrentPosition();
        Log.d(TAG, "----onProgressUpdate-cp---->>>>>>>" + currentPosition);
        if (currentPosition >= (rightProgress)) {
            mPlayView.seekTo((int) leftProgress);
            mIvPosition.clearAnimation();
            if (animator != null && animator.isRunning()) {
                animator.cancel();
            }
            anim();
        }
    }

    private void videoPause() {
        isSeeking = false;
        if (mPlayView != null && mPlayView.isPlaying()) {
            mPlayView.pause();
            handler.removeCallbacks(run);
        }
        Log.d(TAG, "----videoPause----->>>>>>>");
        if (mIvPosition.getVisibility() == View.VISIBLE) {
            mIvPosition.setVisibility(View.GONE);
        }
        mIvPosition.clearAnimation();
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mPlayView != null) {
            mPlayView.seekTo((int) leftProgress);
            videoStart();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        videoPause();
    }

    private Handler handler = new Handler();
    private Runnable run = new Runnable() {

        @Override
        public void run() {
            videoProgressUpdate();
            handler.postDelayed(run, 1000);
        }
    };

    @Override
    protected void onDestroy() {
        FFmpegFunc.getInstance().release();
        NormalProgressDialog.stopLoading();
        if (animator != null) {
            animator.cancel();
        }
        releaseVideoView();
        if (mExtractVideoInfoUtil != null) {
            mExtractVideoInfoUtil.release();
        }
        if (mExtractFrameWorkThread != null) {
            mExtractFrameWorkThread.stopExtract();
        }
        mRecyclerView.removeOnScrollListener(mOnScrollListener);
        mUIHandler.removeCallbacksAndMessages(null);
        handler.removeCallbacksAndMessages(null);
        //删除视频每一帧的预览图
        if (!TextUtils.isEmpty(OutPutFileDirPath)) {
            VideoUtil.deleteFile(new File(OutPutFileDirPath));
        }
        //删除裁剪后的视频，滤镜视频
        String trimmedDirPath = VideoUtil.getTrimmedVideoDir(this, "small_video/trimmedVideo");
        if (!TextUtils.isEmpty(trimmedDirPath)) {
            VideoUtil.deleteFile(new File(trimmedDirPath));
        }
        super.onDestroy();
    }

    //视频处理相关----------------------------------------------------------------------------------
    //使用RxFFmpeg剪裁压缩视频处理方法V2----------------------------------------------------

    /**
     * 视频剪裁和压缩
     */
    private void trimmerAndCompressVideo() {
        //暂停视频
        videoPause();
        NormalProgressDialog
                .showLoading(this, getResources().getString(R.string.in_process), false);
        Log.e(TAG, "trimVideo...startSecond:" + leftProgress + ", endSecond:"
                + rightProgress);
        long startProgress = leftProgress / 1000;
        long endProgress = rightProgress / 1000;
        String videoPath = mVideoPath;
        trimmerAndCompressImpl(videoPath, startProgress, endProgress);
    }

    //V2剪裁视频方法
    protected void trimmerAndCompressImpl(String path, long startPos, long endPos) {
        //先剪裁
        String cropTarget = VideoUtil.getTrimmedVideoPath(this, "crop",
                "trimmedVideo_");
        String startTime = TimeUtils.calHourMinSecond(startPos);
        String endTime = TimeUtils.calHourMinSecond(endPos);
        FFmpegFunc.getInstance().crop(path, startTime, endTime, cropTarget, new FFmpegFunCallback() {
            @Override
            public void failed(String message) {
                Log.i("FFmpegFunc failed", message);
                NormalProgressDialog.stopLoading();
                VideoEditFun.getInstance().notifyError(message);
                editFailed(message);
            }

            @Override
            public void success(String path) {
                Log.i("FFmpegFunc success", path);
                NormalProgressDialog.stopLoading();
                VideoEditFun.getInstance().notifyCropResult(path);
                cropFinish(path);
            }

            @Override
            public void cancel() {
                NormalProgressDialog.stopLoading();
                Log.i("FFmpegFunc cancel", "cancel");
                VideoEditFun.getInstance().notifyError("cancel");
                editFailed("cancel");
            }
        });
    }

    //剪裁完成
    protected void cropFinish(String path) {
        String finalPath = TextUtils.isEmpty(path) ? "" : path;
        Intent resultIntent = new Intent();
        resultIntent.putExtra(PARAMS_CROP_RESULT_PATH, finalPath);
        setResult(TRIM_VIDEO_RES, resultIntent);
        finish();
    }

    //剪裁视频失败
    protected void editFailed(String message) {
        if (!TextUtils.isEmpty(message)) {
            shortToast("视频裁剪失败:" + message);
        }
        finish();
    }

}
